<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>工作5年@Transactional注解用的一塌糊涂</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
        }
        
        .hero-gradient {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }
        
        .content-card {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(10px);
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
        }
        
        .section-title {
            position: relative;
            padding-left: 20px;
        }
        
        .section-title::before {
            content: '';
            position: absolute;
            left: 0;
            top: 50%;
            transform: translateY(-50%);
            width: 4px;
            height: 24px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 2px;
        }
        
        pre {
            background: #1e1e1e;
            color: #d4d4d4;
            border-radius: 8px;
            padding: 20px;
            overflow-x: auto;
            font-size: 14px;
            line-height: 1.6;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
        
        code {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
        }
        
        .inline-code {
            background: #f3f4f6;
            padding: 2px 6px;
            border-radius: 4px;
            color: #e11d48;
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 0.9em;
        }
        
        .highlight-box {
            background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
            border-left: 4px solid #f59e0b;
            padding: 16px 20px;
            margin: 20px 0;
            border-radius: 8px;
        }
        
        .mermaid {
            text-align: center;
            margin: 30px 0;
        }
        
        .floating-icon {
            animation: float 3s ease-in-out infinite;
        }
        
        @keyframes float {
            0%, 100% { transform: translateY(0px); }
            50% { transform: translateY(-10px); }
        }
        
        .card-hover {
            transition: all 0.3s ease;
        }
        
        .card-hover:hover {
            transform: translateY(-5px);
            box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
        }
        
        .drop-cap {
            float: left;
            font-size: 4em;
            line-height: 0.8;
            margin: 0.1em 0.1em 0 0;
            font-weight: 700;
            color: #667eea;
        }
    </style>
</head>
<body>
    <!-- Hero Section -->
    <div class="hero-gradient text-white py-20">
        <div class="container mx-auto px-6 text-center">
            <div class="floating-icon inline-block mb-6">
                <i class="fas fa-code text-6xl opacity-80"></i>
            </div>
            <h1 class="text-5xl font-bold mb-4">工作5年@Transactional注解用的一塌糊涂</h1>
            <p class="text-xl opacity-90 max-w-3xl mx-auto">一个Java老兵的血泪教训：90%的开发者都在错误地使用Spring事务</p>
        </div>
    </div>

    <!-- Main Content -->
    <div class="container mx-auto px-6 py-12">
        <div class="content-card rounded-2xl p-10 mb-8 card-hover">
            <div class="prose max-w-none">
                <p class="text-lg leading-relaxed mb-6">
                    <span class="drop-cap">作</span>为一个在Java圈摸爬滚打10年的后端老兵，我必须说一个残酷的事实：90%的开发者都在错误地使用@Transactional注解！更可怕的是，那些年薪50万的"大神"们犯错的概率反而更高，因为他们往往过于自信。
                </p>
                
                <div class="highlight-box">
                    <p class="text-gray-800 mb-0">
                        <i class="fas fa-exclamation-triangle text-yellow-600 mr-2"></i>
                        我曾经面试过一位自称精通Spring的架构师，当我问他事务传播机制时，他竟然只知道默认的REQUIRED。更讽刺的是，我们生产环境曾因为一个滥用@Transactional的服务损失了近百万交易数据！
                    </p>
                </div>
                
                <p class="text-lg leading-relaxed">
                    有人会说："事务嘛，不就是加个注解的事，搞那么复杂干嘛？"呵呵，等你的应用在高并发下莫名其妙死锁，或者半夜被叫起来修复数据不一致问题时，你就懂了。
                </p>
            </div>
        </div>

        <!-- Section 1: 事务传播机制 -->
        <div class="content-card rounded-2xl p-10 mb-8 card-hover">
            <h2 class="section-title text-3xl font-bold mb-6 text-gray-800">
                <i class="fas fa-network-wired text-purple-600 mr-3"></i>
                1. 事务传播机制
            </h2>
            
            <h3 class="text-2xl font-semibold mb-4 text-gray-700">1.1 事务传播属性的详细解析</h3>
            
            <p class="text-lg leading-relaxed mb-6">
                我曾经问过一个自称有8年开发经验的候选人："Spring提供了几种事务传播行为？"结果对方回答："两种吧，一种是有事务，一种是没事务。"我当场就无语了。Spring实际上提供了7种传播行为，每种都有其特定的应用场景：
            </p>
            
            <pre><code class="language-java">public enum Propagation {
    REQUIRED,        // 默认行为
    SUPPORTS,        // 支持当前事务，没有则以非事务执行
    MANDATORY,       // 必须在事务中执行，否则抛异常
    REQUIRES_NEW,    // 创建新事务，挂起当前事务
    NOT_SUPPORTED,   // 以非事务方式执行，挂起当前事务
    NEVER,           // 以非事务方式执行，有事务则抛异常
    NESTED           // 嵌套事务
}</code></pre>

            <p class="text-lg leading-relaxed mb-6">
                最常用的是<span class="inline-code">REQUIRED</span>和<span class="inline-code">REQUIRES_NEW</span>，但其他的在特定情况下也非常有用。比如<span class="inline-code">NESTED</span>可以在出错时只回滚一部分操作，而<span class="inline-code">MANDATORY</span>可以防止方法在非事务上下文中被调用。
            </p>

            <h3 class="text-2xl font-semibold mb-4 text-gray-700">1.2 常见误区：PROPAGATION_REQUIRED与PROPAGATION_REQUIRES_NEW的区别</h3>
            
            <p class="text-lg leading-relaxed mb-6">
                有一次我参与重构一个支付系统，发现前任开发者混用了这两个传播属性，导致系统出现了严重的数据不一致问题。看看这个例子：
            </p>
            
            <pre><code class="language-java">@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 保存订单
        orderRepository.save(order);
        // 扣减库存
        inventoryService.reduce(order.getProductId(), order.getQuantity());
        // 处理支付
        paymentService.processPayment(order.getId(), order.getAmount());
    }
}

@Service
public class PaymentService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Long orderId, BigDecimal amount) {
        // 支付逻辑
        paymentRepository.savePayment(new Payment(orderId, amount));
    }
}</code></pre>

            <div class="bg-red-50 border-l-4 border-red-500 p-6 rounded-r-lg my-6">
                <p class="text-lg text-red-800">
                    <i class="fas fa-bug text-red-600 mr-2"></i>
                    这看起来没问题，但如果<span class="inline-code">inventoryService.reduce()</span>发生异常，<span class="inline-code">createOrder()</span>会回滚，但<span class="inline-code">processPayment()</span>不会回滚，因为它在一个独立的事务中！结果就是：订单和库存都回滚了，但支付记录依然存在。恭喜你，刚刚制造了一个数据不一致的典型案例。
                </p>
            </div>
        </div>

        <!-- Section 2: 类内部方法调用 -->
        <div class="content-card rounded-2xl p-10 mb-8 card-hover">
            <h2 class="section-title text-3xl font-bold mb-6 text-gray-800">
                <i class="fas fa-exclamation-circle text-purple-600 mr-3"></i>
                2. 类内部方法调用
            </h2>
            
            <h3 class="text-2xl font-semibold mb-4 text-gray-700">2.1 为什么类内部调用会导致@Transactional失效</h3>
            
            <p class="text-lg leading-relaxed mb-6">
                这是我见过的最常见错误，也是最难排查的：
            </p>
            
            <pre><code class="language-java">@Service
public class UserService {
    @Transactional
    public void updateUser(User user) {
        // 更新用户信息
    }
    
    public void registerUser(User user) {
        // 一些非事务操作
        updateUser(user); // 这里的事务会失效！
    }
}</code></pre>

            <p class="text-lg leading-relaxed mb-6">
                很多开发者不知道，<span class="inline-code">registerUser()</span>内部调用<span class="inline-code">updateUser()</span>时，事务是不生效的！原因是Spring的AOP代理机制导致的。
            </p>

            <h3 class="text-2xl font-semibold mb-4 text-gray-700">2.2 代理机制的工作原理</h3>
            
            <div class="mermaid">
                <script>
                mermaid.initialize({ theme: 'default' });
                </script>
                <div class="mermaid">
                graph LR
                    A[外部调用] --> B[代理对象]
                    B --> C[开启事务]
                    C --> D[目标方法]
                    D --> E[提交/回滚事务]
                    
                    F[内部调用] --> G[目标方法]
                    
                    style A fill:#667eea,stroke:#333,stroke-width:2px,color:#fff
                    style B fill:#764ba2,stroke:#333,stroke-width:2px,color:#fff
                    style C fill:#f59e0b,stroke:#333,stroke-width:2px,color:#fff
                    style E fill:#f59e0b,stroke:#333,stroke-width:2px,color:#fff
                    style F fill:#ef4444,stroke:#333,stroke-width:2px,color:#fff
                    style G fill:#6b7280,stroke:#333,stroke-width:2px,color:#fff
                </div>
            </div>

            <p class="text-lg leading-relaxed mb-6">
                Spring事务是通过AOP实现的，它会为标记了@Transactional的Bean创建一个代理。当外部调用这个Bean的方法时，实际上是调用了代理对象，代理会在调用目标方法前后添加事务处理逻辑。但当在Bean内部调用自己的方法时，这个调用不会经过代理，而是直接调用目标方法，导致事务注解失效。
            </p>

            <h3 class="text-2xl font-semibold mb-4 text-gray-700">2.3 如何正确设计类结构以避免事务失效</h3>
            
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
                <div class="bg-green-50 p-6 rounded-lg">
                    <h4 class="text-xl font-semibold mb-3 text-green-